package dcutils;

/**
 * The Range class represents a comparable set, beginning to end.<br/>
 * E.g.:
<pre style='color:#d1d1d1;background:#000000;'>
Range&lt;Integer> rInt = new Range&lt;Integer>(-100, 100)<span style='color:#d2cd86; '>;</span>
rInt.withinRange(100, true)<span style='color:#d2cd86; '>;</span> <span style='color:#9999a9; '>// inclusive=true.  This returns true.</span>
rInt.withinRange(100, false)<span style='color:#d2cd86; '>;</span> <span style='color:#9999a9; '>// inclusive=false.  This returns false.</span>
rInt.lessThanRange(0, true)<span style='color:#d2cd86; '>;</span> <span style='color:#9999a9; '>// inclusive=true.  This returns false.</span>
rInt.lessThanRange(-500, true)<span style='color:#d2cd86; '>;</span> <span style='color:#9999a9; '>// inclusive=true.  This returns true.</span>
rInt.greaterThanRange(0, true)<span style='color:#d2cd86; '>;</span> <span style='color:#9999a9; '>// inclusive=true.  This returns false.</span>
rInt.greaterThanRange(500, true)<span style='color:#d2cd86; '>;</span> <span style='color:#9999a9; '>// inclusive=true.  This returns true.</span>
</pre>
 * @author dca
 */
public class Range<T extends Comparable<T>> {
	public static class RangeException extends Exception {
		private static final long serialVersionUID = 1351045561755212060L;
		public RangeException() { super(); }
		public RangeException(String message) { super(message); }
		public RangeException(String message, Throwable cause) { super(message, cause); }
		public RangeException(Throwable cause) { super(cause); }
	} // END inner class RangeException

	/** Represents the beginning of the range.<br/> */
	private T beginning;

	/** Represents the ending of the range.<br/> */
	private T ending;

	/**
	 * Creates a new instance of this class.<br/>
	 * @param beginning The beginning of the range.  Must be less than <code>ending</code>.<br/>
	 * @param ending The ending of the range.  Must be greater than <code>beginning</code>.<br/>
	 * @throws RangeException 
	 */
	public Range(T beginning, T ending) throws RangeException {
		if(null == beginning || null == ending) {
			throw new RangeException("Invalid parameters: Either beginning or ending is null.");
		} // END if

		if(beginning.compareTo(ending) > 0) {
			throw new RangeException("Invalid parameters: Beginning is greater than ending: " + beginning + " > " + ending + ".");
		} // END if

		this.beginning = beginning;
		this.ending = ending;
	} // END constructor

	/**
	 * Checks whether or not the <code>item</code> is within range.<br/>
	 * @param item The comparable item to check.<br/>
	 * @param inclusive If true, then item will be considered &apos;within range&apos; even if it is the same as <code>beginning</code> or <code>ending</code>.<br/>
	 * @return True or false.<br/>
	 */
	public boolean withinRange(T item, boolean inclusive) {
		if(null == item) {
			return false;
		} else if(inclusive) {
			return item.compareTo(beginning) >= 0 && item.compareTo(ending) <= 0;
		} else {
			return item.compareTo(beginning) > 0 && item.compareTo(ending) < 0;
		} // END if/else
	} // END withinRange

	/**
	 * Checks whether or not the <code>item</code> is less than the range.<br/>
	 * @param item The comparable item to check.<br/>
	 * @param inclusive If true, then item will be considered &apos;less than the range&apos; even if it is the same as <code>beginning</code> or <code>ending</code>.<br/>
	 * @return True or false.<br/>
	 */
	public boolean lessThanRange(T item, boolean inclusive) {
		if(null == item) {
			return false;
		} else if(inclusive) {
			return item.compareTo(beginning) <= 0;
		} else {
			return item.compareTo(beginning) < 0;
		} // END if/else
	} // END lessThanRange

	/**
	 * Checks whether or not the <code>item</code> is greater than the range.<br/>
	 * @param item The comparable item to check.<br/>
	 * @param inclusive If true, then item will be considered &apos;greater than the range&apos; even if it is the same as <code>beginning</code> or <code>ending</code>.<br/>
	 * @return True or false.<br/>
	 */
	public boolean greaterThanRange(T item, boolean inclusive) {
		if(null == item) {
			return false;
		} else if(inclusive) {
			return item.compareTo(ending) >= 0;
		} else {
			return item.compareTo(ending) > 0;
		} // END if/else
	} // END greaterThanRange

	/**
	 * Checks whether or not the <code>otherRange</code> is less than this range.<br/>
	 * @param otherRange The other range to check.<br/>
	 * @param inclusive If true, then otherRange will be considered &apos;less than&apos; even if its ends are the same as <code>beginning</code> and <code>ending</code>.<br/>
	 * @return True or false.<br/>
	 */
	public boolean lessThanRange(Range<T> otherRange, boolean inclusive) {
		if(null == otherRange) {
			return false;
		} else {
			return lessThanRange(otherRange.getBeginning(), inclusive) && lessThanRange(otherRange.getEnding(), inclusive);
		} // END if/else
	} // END lessThanRange

	/**
	 * Checks whether or not the <code>otherRange</code> is greater than this range.<br/>
	 * @param otherRange The other range to check.<br/>
	 * @param inclusive If true, then otherRange will be considered &apos;greater than&apos; even if its ends are the same as <code>beginning</code> and <code>ending</code>.<br/>
	 * @return True or false.<br/>
	 */
	public boolean greaterThanRange(Range<T> otherRange, boolean inclusive) {
		if(null == otherRange) {
			return false;
		} else {
			return greaterThanRange(otherRange.getBeginning(), inclusive) && greaterThanRange(otherRange.getEnding(), inclusive);
		} // END if/else
	} // END lessThanRange

	/**
	 * Checks whether or not the <code>otherRange</code> overlaps this range.<br/>
	 * @param otherRange The other range to check.<br/>
	 * @param inclusive If true, then otherRange will be considered &apos;overlapping&apos; even if its ends are the same as <code>beginning</code> and <code>ending</code>.<br/>
	 * @return True or false.<br/>
	 */
	public boolean overlaps(Range<T> otherRange, boolean inclusive) {
		if(null == otherRange) {
			return false;
		} else {
			return withinRange(otherRange.getBeginning(), inclusive) ||
			       withinRange(otherRange.getEnding(),    inclusive) ||
			       isSubrange(otherRange,   inclusive) ||
			       isSuperrange(otherRange, inclusive);
		} // END if/else
	} // END overlaps

	/**
	 * Checks whether or not the <code>otherRange</code> is a subrange of this range.<br/>
	 * @param otherRange The other range to check.<br/>
	 * @param inclusive If true, then otherRange will be considered a &apos;subrange&apos; even if its ends are the same as <code>beginning</code> and <code>ending</code>.<br/>
	 * @return True or false.<br/>
	 */
	public boolean isSubrange(Range<T> otherRange, boolean inclusive) {
		if(null == otherRange) {
			return false;
		} else {
			return withinRange(otherRange.getBeginning(), inclusive) &&
			       withinRange(otherRange.getEnding(),    inclusive);
		} // END if/else
	} // END isSubrange

	/**
	 * Checks whether or not the <code>otherRange</code> is a super-range of this range.<br/>
	 * @param otherRange The other range to check.<br/>
	 * @param inclusive If true, then otherRange will be considered a &apos;super-range&apos; even if its ends are the same as <code>beginning</code> and <code>ending</code>.<br/>
	 * @return True or false.<br/>
	 */
	public boolean isSuperrange(Range<T> otherRange, boolean inclusive) {
		if(null == otherRange) {
			return false;
		} else {
			return lessThanRange(otherRange.getBeginning(), inclusive) &&
			       greaterThanRange(otherRange.getEnding(), inclusive);
		} // END if/else
	} // END isSuperrange

	/**
	 * Returns <code>beginning</code>.<br/>
	 * @return <code>beginning</code>.<br/>
	 */
	public T getBeginning() {
		return beginning;
	} // END getBeginning

	public Range<T> setBeginning(T beginning) throws RangeException {
		if(null == beginning) {
			throw new RangeException("Invalid parameter: Beginning is null.");
		} // END if

		if(beginning.compareTo(ending) > 0) {
			throw new RangeException("Invalid parameters: Beginning is greater than ending: " + beginning + " > " + ending + ".");
		} // END if

		this.beginning = beginning;
		return this;
	} // END setBeginning

	/**
	 * Returns <code>ending</code>.<br/>
	 * @return <code>ending</code>.<br/>
	 */
	public T getEnding() {
		return ending;
	} // END getEnding

	public Range<T> setEnding(T ending) throws RangeException {
		if(null == ending) {
			throw new RangeException("Invalid parameter: Ending is null.");
		} // END if

		if(beginning.compareTo(ending) > 0) {
			throw new RangeException("Invalid parameters: Beginning is greater than ending: " + beginning + " > " + ending + ".");
		} // END if

		this.ending = ending;
		return this;
	} // END setEnding
} // END class Range
